﻿--[[--------------------------------------------------------------------
	GridCodeCore.lua
----------------------------------------------------------------------]]

local GRIDCODE, GridCode = ...

if not GridCode.L then GridCode.L = { } end
local L = setmetatable( GridCode.L, {
	__index = function(t, k)
		t[k] = k
		return k
	end
})

_G.GridCode = LibStub("AceAddon-3.0"):NewAddon(GridCode, "GridCode", "AceConsole-3.0", "AceEvent-3.0")

------------------------------------------------------------------------

GridCode.debugFrame = ChatFrame1

function GridCode:Debug(str, ...)
	if not self.debugging then return end
	if not str or str:len() == 0 then return end
	if (...) then
		if str:find("%%[dfs%d%.]") then
			str = str:format(...)
		else
			str = strjoin(" ", str, tostringall(...))
		end
	end
	self.debugFrame:AddMessage("|cffff9933GridCode:|r " .. str)
end

------------------------------------------------------------------------

GridCode.options = {
	handler = GridCode,
	type = "group",
	--childGroups = "tab",
	args = {
		["debug"] = {
			type = "group",
			name = L["Debugging"],
			desc = L["Module debugging menu."],
			order = -1,
			args = {},
		},
	},
}

------------------------------------------------------------------------

GridCode.defaults = {
	debug = false,
}

------------------------------------------------------------------------

GridCode.modulePrototype = {
	core = GridCode,
	Debug = GridCode.Debug,
	registeredModules = { },
}

function GridCode.modulePrototype:OnInitialize()
	if not self.db then
		self.db = self.core.db:RegisterNamespace(self.moduleName, { profile = self.defaultDB or { } })
	end

	self.debugFrame = GridCode.debugFrame
	self.debugging = self.db.profile.debug

	self:Debug("OnInitialize")
	self.core:AddModuleDebugMenu(self)

	for name, module in self:IterateModules() do
		self:RegisterModule(name, module)
	end

	if type(self.PostInitialize) == "function" then
		self:PostInitialize()
	end
end

function GridCode.modulePrototype:OnEnable()
	for name, module in self:IterateModules() do
		self:RegisterModule(name, module)
	end

	self:EnableModules()

	if type(self.PostEnable) == "function" then
		self:PostEnable()
	end
end

function GridCode.modulePrototype:OnDisable()
	self:DisableModules()

	if type(self.PostDisable) == "function" then
		self:PostDisable()
	end
end

function GridCode.modulePrototype:Reset()
	self.debugging = self.db.profile.debug
	self:Debug("Reset")
	self:ResetModules()

	if type(self.PostReset) == "function" then
		self:PostReset()
	end
end

function GridCode.modulePrototype:OnModuleCreated(module)
	module.super = self.modulePrototype
end

function GridCode.modulePrototype:RegisterModule(name, module)
	if self.registeredModules[module] then return end

	self:Debug("Registering", name)

	if not module.db then
		module.db = GridCode.db:RegisterNamespace(name, { profile = module.defaultDB or { } })
	end

	if module.extraOptions and not module.options then
		module.options = {
			name = module.menuName or module.moduleName,
			desc = string.format(L["Options for %s."], module.moduleName),
			type = "group",
			args = {},
		}
		for name, option in pairs(module.extraOptions) do
			module.options.args[name] = option
		end
	end

	if module.options then
		self.options.args[name] = module.options
	end

	self.core:AddModuleDebugMenu(module)

	self.registeredModules[module] = true
end

function GridCode.modulePrototype:EnableModules()
	for name, module in self:IterateModules() do
		self:EnableModule(name)
	end
end

function GridCode.modulePrototype:DisableModules()
	for name, module in self:IterateModules() do
		self:DisableModule(name)
	end
end

function GridCode.modulePrototype:ResetModules()
    for name, module in self:IterateModules() do
		self:Debug("Resetting " .. name)
		module.db = self.core.db:GetNamespace(name)
		module:Reset()
    end
end

GridCode:SetDefaultModulePrototype(GridCode.modulePrototype)
GridCode:SetDefaultModuleLibraries("AceEvent-3.0")

------------------------------------------------------------------------

function GridCode:OnChatCommand(input)
	if not input or input:trim() == "" then
		local AceConfigDialog = LibStub("AceConfigDialog-3.0")
		AceConfigDialog:Open(L["GridCode"])
	else
		local AceConfigCmd = LibStub("AceConfigCmd-3.0")
		AceConfigCmd.HandleCommand(GridCode, "gridcode", L["GridCode"], input)
	end
end

function GridCode:OnInitialize()
	self.db = LibStub("AceDB-3.0"):New("GridCodeDB", { profile = self.defaults }, true)

	self.db.RegisterCallback(self, "OnProfileChanged", "OnProfileEnable")
	self.db.RegisterCallback(self, "OnProfileCopied", "OnProfileEnable")
	self.db.RegisterCallback(self, "OnProfileReset", "OnProfileEnable")

	self.options.args.profile = LibStub("AceDBOptions-3.0"):GetOptionsTable(self.db)
	self.options.args.profile.order = -2

	local LibDualSpec = LibStub("LibDualSpec-1.0")
	LibDualSpec:EnhanceDatabase(self.db, "GridCode")
	LibDualSpec:EnhanceOptions(self.options.args.profile, self.db)

	LibStub("AceConfigRegistry-3.0"):RegisterOptionsTable(L["GridCode"], self.options)

	local AceConfigCmd = LibStub("AceConfigCmd-3.0")
	local AceConfigDialog = LibStub("AceConfigDialog-3.0")

	self.optionsFrame = AceConfigDialog:AddToBlizOptions("GridCode", "GridCode")

	local status = AceConfigDialog:GetStatusTable(L["GridCode"])
	status.width = 685 -- 685
	status.height = 486 -- 530

	local child1 = AceConfigDialog:GetStatusTable(L["GridCode"], { "Indicators" })
	child1.groups = child1.groups or { }
	child1.groups.treewidth = 220

	local child2 = AceConfigDialog:GetStatusTable(L["GridCode"], { "GridCodeStatus" })
	child2.groups = child2.groups or { }
	child2.groups["GridStatusAuras"] = true
	child2.groups["GridStatusHealth"] = true
	child2.groups["GridStatusRange"] = true
	child2.groups.treewidth = 260

	self:RegisterChatCommand("gridcode", "OnChatCommand")
	self:RegisterChatCommand("gcode", "OnChatCommand")
	self:RegisterChatCommand("gcd", "OnChatCommand")

	InterfaceOptionsFrame:HookScript("OnShow", function()
		AceConfigDialog:Close(L["GridCode"])
	end)

	-- we need to save debugging state over sessions :(
	self.debugging = self.db.profile.debug

	self:AddModuleDebugMenu(self)

	for name, module in self:IterateModules() do
		self:RegisterModule(name, module)
	end

	-- to catch mysteriously late-loading modules
	self:RegisterEvent("ADDON_LOADED")
end

function GridCode:OnEnable()
	self:Debug("OnEnable")

	for name, module in self:IterateModules() do
		self:RegisterModule(name, module)
	end

	--self:EnableModules()

	self:RegisterEvent("PLAYER_ENTERING_WORLD")
	self:RegisterEvent("PLAYER_REGEN_DISABLED")
	self:RegisterEvent("PLAYER_REGEN_ENABLED")

	--self:SendMessage("GridCode_Enabled")
end

function GridCode:OnDisable()
	self:Debug("OnDisable")

	--self:SendMessage("GridCode_Disabled")

	--self:DisableModules()
end

function GridCode:OnProfileEnable()
	self.debugging = self.db.profile.debug
	self:Debug("Loaded profile", self.db:GetCurrentProfile())
	self:ResetModules()
end

function GridCode:OnModuleCreated(module)
	module.super = self.modulePrototype
end

------------------------------------------------------------------------

local registeredModules = { }

function GridCode:RegisterModule(name, module)
	if registeredModules[module] then return end

	self:Debug("Registering " .. name)

	if not module.db then
		module.db = self.db:RegisterNamespace(name, { profile = module.defaultDB or { } })
	end

	if module.options then
		self.options.args[name] = module.options
	end

	self:AddModuleDebugMenu(module)

	registeredModules[module] = true
end

function GridCode:AddModuleDebugMenu(module)
	local debugMenu = GridCode.options.args.debug

	debugMenu.args[module.moduleName or module.name] = {
		name = module.moduleName or module.name,
		desc = string.format(L["Toggle debugging for %s."], module.moduleName or module.name),
		type = "toggle", width = "double",
		get = function()
			return module.db.profile.debug
		end,
		set = function(_, v)
			module.db.profile.debug = v
			module.debugging = v
		end,
	}
end

function GridCode:EnableModules()
	for name, module in self:IterateModules() do
		self:EnableModule(name)
	end
end

function GridCode:DisableModules()
	for name, module in self:IterateModules() do
		self:DisableModule(name)
	end
end

function GridCode:ResetModules()
	for name, module in self:IterateModules() do
		self:Debug("Resetting " .. name)
		module.db = self.db:GetNamespace(name)
		module:Reset()
	end
end

------------------------------------------------------------------------

function GridCode:PLAYER_ENTERING_WORLD()
	-- this is needed for zoning while in combat
	self:PLAYER_REGEN_ENABLED()
end

function GridCode:PLAYER_REGEN_DISABLED()
	self:Debug("Entering combat")
	self:SendMessage("GridCode_EnteringCombat")
	GridCode.inCombat = true
end

function GridCode:PLAYER_REGEN_ENABLED()
	GridCode.inCombat = InCombatLockdown() == 1
	if not GridCode.inCombat then
		self:Debug("Leaving combat")
		self:SendMessage("GridCode_LeavingCombat")
	end
end

function GridCode:ADDON_LOADED()
	for name, module in self:IterateModules() do
		self:RegisterModule(name, module)
	end
end

function GridCode:Toggle(switch)
	if (switch) then
		local name,IsEnable
		for i=1, GetNumAddOns() do
			name,_,_,IsEnable = GetAddOnInfo(i);
			if string.find(name,"GridCode") and IsEnable then
				if (not IsAddOnLoaded(name)) then
					LoadAddOn(name);
				end
			end
		end
		if (not IsAddOnLoaded("GridCodeExtend")) then
			LoadAddOn("GridCodeExtend");
		end
		--GridCodeClickSetsFrame_ApplyOnClick(true)
		self:EnableModules()
		self:SendMessage("GridCode_Enabled")
		self:ResetModules();
	else
		self:SendMessage("GridCode_Disabled")
		self:DisableModules()
	end
end

function GridCode_OpenConfig()
	LibStub("AceConfigDialog-3.0"):Open(L["GridCode"])
end

function GridCode_CodeInfo_update()
	self:Debug("GridCode_CodeInfo_update()")
end
